#include <stdio.h>
#include <assert.h>
#include "modem.h"
#include "codec2.h"
#include "config.h"

/*
this proof-of-concept implementation was written by Peter Lawrence

Copyright 2012
Peter Lawrence
All Rights Reserved

no warranty is provided with this code
*/

/* expected by newqpsk library */
unsigned int log_verblevel;

/* struct of the user's creation that newqpsk passes a pointer back through audiowrite() */
struct modemchannel
{
	struct modulator *mod;
};

/* raw_bps, obsolete, obsolete, tune_len, sync_len */
const char *params[5] = { "2500", NULL, NULL, "32", "32" };
//const char *params[5] = { "2500", NULL, NULL, "0", "0" };

static short *speech;
static u_int8_t *c2frame;
static FILE *input;
static void *codec2_ctx;

int main(int argc, char *argv[])
{
	struct modemchannel chan;
	void *ctx;
	unsigned int sample_rate;
	int nr, pos;
	signed short *samples;
	FILE *output;

	if (argc < 3)
	{
		fprintf(stderr, "%s <input_speech_file> <output_modem_audio>\n", argv[0]);
		return -1;
	}

	if (NULL == (input = fopen(argv[1], "rb")))
	{
		fprintf(stderr, "FATAL: unable to open input speech file (%s)\n", argv[1]);
		return -2;
	}

	/* open the file to write the audio to */
	if (NULL == (output = fopen(argv[2], "wb")))
	{
		fprintf(stderr, "FATAL: unable to open output modem audio file (%s)\n", argv[2]);
		return -3;
	}

	codec2_ctx = codec2_create(CODEC2_MODE_2500);

	if (!codec2_ctx)
		goto unexpected_error;

	speech = (short *)malloc(codec2_samples_per_frame(codec2_ctx) * sizeof(short));
	c2frame = (unsigned char *)malloc((codec2_bits_per_frame(codec2_ctx) + 7) / 8);

	if (!speech || !c2frame)
		goto unexpected_error;

	/* select the modulator of choice (this is a legacy of the soundmodem library) */
	chan.mod = &newqpskmodulator;

	/* config (and be informed of the sample rate) */
	ctx = chan.mod->config(&chan, &sample_rate, params);

	sample_rate = OFDM_SAMPLE_RATE;

	/* init comes after config */
	chan.mod->init(ctx, sample_rate);

	pos = 0;

	for (;;)
	{
		/* perform the modulation */
		nr = chan.mod->modulate(ctx, &samples);

		if (nr)
		{
			fwrite(samples, nr, sizeof(int16_t), output);
		}
		else
			break;

	}

	/* close the audio file */
	fclose(output);

	goto finish;

unexpected_error:
	fprintf(stderr, "FATAL: unexpected error\n");

finish:
	free(speech);
	free(c2frame);
	codec2_destroy(codec2_ctx);

	return 0;
}

/* proof of concept getwords() that just uses an incrementing counter as a data source */

int getwords(struct modemchannel *chan, unsigned *words, unsigned int len)
{
	int outcome = 0;

	assert(2 == len);

	if (fread(speech, 1, codec2_samples_per_frame(codec2_ctx) * sizeof(short), input))
	{
		codec2_encode(codec2_ctx, c2frame, speech);

		words[0] = c2frame[0];
		words[0] <<= 8;
		words[0] += c2frame[1];
		words[0] <<= 8;
		words[0] += c2frame[2];
		words[0] <<= 1;
		if (c2frame[6] & 0x80)
			words[0] |= 1;

		words[1] = c2frame[3];
		words[1] <<= 8;
		words[1] += c2frame[4];
		words[1] <<= 8;
		words[1] += c2frame[5];
		words[1] <<= 1;
		if (c2frame[6] & 0x40)
			words[1] |= 1;

		outcome = 1;
	}

	return outcome;
}

/* library expects logprintf(); this directs the output to stderr */

void logprintf(unsigned int level, const char *fmt, ...)
{
	va_list arg;

	fprintf(stderr, "lev%u: ", level);
	va_start(arg, fmt);
	vfprintf(stderr, fmt, arg);
	va_end(arg);
}
